﻿using JESAI.DistributedLock.Internal;
using JESAI.DistributedLock.Internal.Data;
using System;
using System.Data;
using System.Threading;
using System.Threading.Tasks;
#if NET7_0_OR_GREATER
using System.Data.Common;
#endif

namespace JESAI.DistributedLock.Postgres;

/// <summary>
/// 使用Postgres顾问锁实现分布式锁
///（请参见https://www.postgresql.org/docs/12/functions-admin.html#FUNCTIONS-ADVISORY-LOCKS）
/// </summary>
public sealed partial class PostgresDistributedReaderWriterLock : IInternalDistributedReaderWriterLock<PostgresDistributedReaderWriterLockHandle>
{
    private readonly IDbDistributedLock _internalLock;

    /// <summary>
    /// 使用给定的<paramref name="key"/>（实际上是锁名称）、<paramref name="connectionString"/>和<paramef name="options"/>构造锁
    /// </summary>
    public PostgresDistributedReaderWriterLock(PostgresAdvisoryLockKey key, string connectionString, Action<PostgresConnectionOptionsBuilder>? options = null)
        : this(key, PostgresDistributedLock.CreateInternalLock(key, connectionString, options))
    {
    }

    /// <summary>
    /// 使用给定的<paramref name="key"/>（实际上是锁名称）和<paramref name="connection"/>.构造锁。
    /// </summary>
    public PostgresDistributedReaderWriterLock(PostgresAdvisoryLockKey key, IDbConnection connection)
        : this(key, PostgresDistributedLock.CreateInternalLock(key, connection))
    {
    }

#if NET7_0_OR_GREATER
    /// <summary>
    /// 使用给定的<paramref name="key"/>（实际上是锁名称）和<paramref name="dbDataSource"/><paramef name="options"/>构造锁。
    /// 
    /// 与连接多路复用不兼容。
    /// </summary>
    public PostgresDistributedReaderWriterLock(PostgresAdvisoryLockKey key, DbDataSource dbDataSource, Action<PostgresConnectionOptionsBuilder>? options = null)
        : this(key, PostgresDistributedLock.CreateInternalLock(key, dbDataSource, options))
    {
    }
#endif

    private PostgresDistributedReaderWriterLock(PostgresAdvisoryLockKey key, IDbDistributedLock internalLock)
    {
        this.Key = key;
        this._internalLock = internalLock;
    }

    /// <summary>
    /// 唯一标识数据库上的锁的<see cref=“PostgresAdvisoryLockKey”/>
    /// </summary>
    public PostgresAdvisoryLockKey Key { get; }

    string IDistributedReaderWriterLock.Name => this.Key.ToString();

    ValueTask<PostgresDistributedReaderWriterLockHandle?> IInternalDistributedReaderWriterLock<PostgresDistributedReaderWriterLockHandle>.InternalTryAcquireAsync(
        TimeoutValue timeout,
        CancellationToken cancellationToken,
        bool isWrite) =>
        this._internalLock.TryAcquireAsync(timeout, isWrite ? PostgresAdvisoryLock.ExclusiveLock : PostgresAdvisoryLock.SharedLock, cancellationToken, contextHandle: null)
            .Wrap(h => new PostgresDistributedReaderWriterLockHandle(h));
}